#include <string.h>

#include "lib.h"

#include "optplyr.h"
#include "opthelpr.h"
#include "striter.h"
#include "strftest.h"
#include "getfullh.h"
#include "getnpair.h"
#include "pghand.h"
#include "jokcrdmg.h"

//
// NOTE: CODE ASSUMES THERE IS ONLY *ONE* JOKER! (as it should be)
//

static uint8 sTwoPairAction[13][13] =	// [highpair][lowpair]
{
// split=0xff, impossible=0
// ace=0xe, king=0xd, queen=0xc, jack=0xb, ten=0xa, ...
//
//high	low
//pair  pair -->
/*2*/	{ 0 },
/*3*/	{ 0xba, 0 },
/*4*/	{ 0xc5,0xc6, 0 },
/*5*/	{ 0xca,0xca,0xcb, 0 },
/*6*/	{ 0xcb,0xd4,0xd3,0xd3, 0 },
/*7*/	{ 0xd4,0xd4,0xd3,0xda, 0xdb, 0 },
/*8*/	{ 0xd4,0xd5,0xdb,0xdb, 0xdc,0xdc, 0 },
/*9*/	{ 0xda,0xdb,0xdc,0xdc, 0xdc,0xe3,0xe3, 0 },
/*10*/	{ 0xdc,0xdc,0xdc,0xe3, 0xe3,0xe6,0xeb,0xeb, 0 },
/*J*/	{ 0xe4,0xe4,0xe3,0xe8, 0xea,0xec,0xec,0xec, 0xed, 0 },
/*Q*/	{ 0xe8,0xe9,0xea,0xeb, 0xed,0xed,0xed,0xed, 0xff,0xff, 0 },
/*K*/	{ 0xeb,0xeb,0xec,0xec, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff, 0 },
/*A*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
};

static uint8 sFourOfAKindAndSingletonsAction[13] =	// [four-of-a-kind]
{
// split=0xff, hold=0
// ace=0xe, king=0xd, queen=0xc, jack=0xb, ten=0xa, ...
// 
//four		high
//ofkind	singletons
/*2*/		0,
/*3*/		0,
/*4*/		0,
/*5*/		0,
/*6*/		0,
/*7*/		0xc3,
/*8*/		0xcb,
/*9*/		0xd3,
/*10*/		0xdc,
/*J*/		0xe3,
/*Q*/		0xea,
/*K*/		0xec,
/*A*/		0xff
};

static uint8 sFullHouseAndSingletonsAction[13][13] = // [three-of-a-kind][pair]
{
// kharmon.  03-31-06.  #9473.  Corrected split settings for three 4's and two 2's.
// split=0xff, impossible=0
// ace=0xe, king=0xd, queen=0xc, jack=0xb, ten=0xa, ...
//
//three
//kind  pair -->
/*2*/	{ 0   ,0xea,0xeb,0xec, 0xec,0xed,0xed,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*3*/	{ 0xea,0   ,0xeb,0xec, 0xec,0xed,0xed,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*4*/	{ 0xeb,0xec,0   ,0xec, 0xed,0xed,0xed,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*5*/	{ 0xeb,0xec,0xec,0   , 0xed,0xed,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*6*/	{ 0xeb,0xec,0xec,0xed, 0   ,0xed,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*7*/	{ 0xec,0xec,0xed,0xed, 0xed,0   ,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*8*/	{ 0xec,0xec,0xed,0xed, 0xed,0xff,0   ,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*9*/	{ 0xec,0xec,0xed,0xed, 0xed,0xff,0xff,0   , 0xff,0xff,0xff,0xff, 0xff },
/*10*/	{ 0xec,0xec,0xed,0xed, 0xff,0xff,0xff,0xff, 0   ,0xff,0xff,0xff, 0xff },
/*J*/	{ 0xec,0xed,0xed,0xed, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*Q*/	{ 0xeb,0xed,0xed,0xed, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*K*/	{ 0xeb,0xec,0xec,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*A*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff }
};

static uint8 sFlushPairNoJokerAction[13][13] = //[2nd top flush singleton][pair]
{
// fivecard the flush=0xff, fivecard the pair=1, impossible=0
// ace=0xe, king=0xd, queen=0xc, jack=0xb, ten=0xa, ...
// 
// Table is really indexed by [ACE and XXX][pair] with ACE and XXX both part of
// the flush; neither are part of the pair.  But, it's much easier to prescreen
// the top non-pair card of the flush and not use the table if it's not an ACE.
//
// So the table as presented is indexed by the 2nd top flush singleton.
//
//2nd
//top	pair -->
/*2*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*3*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*4*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*5*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*6*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*7*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*8*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*9*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*10*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
/*J*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0x07, 0x09,0xff,0xff,0xff, 0xff },
/*Q*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0x09,0x0a, 0x09,0x0a,0xff,0xff, 0xff },
/*K*/	{ 0xff,0xff,0xff,0xff, 0xff,0x0a,0x0a,0x0b, 0x0b,0x0a,   1,0xff, 0xff },
/*A*/	{ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff },
};

class OPState
{
	public:
		OPState(PGPokerHand*, int32*);

		void categorizeCards();
		void splitHand();

	protected:
		bool8 hasStraight(int32*, bool8*);
		bool8 hasFlush(bool8*);
		bool8 has_N_ofAKindNoJoker(int32);
		bool8 has_N_ofAKindJoker(int32);
		bool8 has_N_ofAKind(int32, bool8*);
		bool8 hasFullHouse();

		void handleStraight(int32);
		void handleFlush(bool8);
		void handleFullHouse();
		void handle5ofAKind();
		void handle4ofAKind(bool8);
		void handle3ofAKind(bool8);
		void handleNPair();
		void handleHighCard();

	private:
		void handleStraightAndFlushAndFullHouse();
		void handleStraightAndFlushAnd3ofAKind();
		void handleStraightAndFlushAndNPair(int32);
		void handleStraightAndFlush();
		void handleStraightAndFullHouse();
		void handleStraightAnd3ofAKind();
		void handleStraightAndNPair();

		bool8 hasStraightWithout(int32*);
		void fiveCardTheStraight();
		uint8* findNthNonStraightCard(int32, int32, int32);
		int32 findLongestStraight(int32*, uint8**);

		void setStraightAndFlush_Wong13(PGPokerHandRanks*, PGPokerHandRanks*);

		int32 compareHandRanks(PGPokerHandRanks*, PGPokerHandRanks*);

		void setJokerStraightPair_JokerPair(GetNPair*);
		void setJokerStraightPair_NonJokerPair(GetNPair*);
		void setJokerStraightFlushPair_JokerPair(GetNPair*);
		void setJokerStraightFlushPair_NonJokerPair(GetNPair*);
		void setStraightAndFlushAndPair(GetNPair*);

		void handleFlushAndFullHouse(bool8);
		void handleFlushAnd3ofAKind(bool8);
		void handleFlushAndNPair(bool8);

		int32 getCardIndex(int32, int32);
		uint8* findNthHighestCard(int32, int32*);

		bool8 hasFlushWithout(int32*);
		void fiveCardTheFlush();

		void fiveCardTheOnlyPair(GetNPair*);

		void setUsingTwoPairActionTable(uint8*[3], uint8*[2]);
		void setUsingFourOfAKindAndSingletonsActionTable(uint8*, uint8*[2]);
		void setUsingFullHouseAndSingletonsActionTable(
			uint8*, uint8*, uint8*[2], bool8);
		void setUsingFlushPairNoJokerActionTable(int32, uint8*);
		void setUsingFlushPairNoJokerActionTableWithJoker(int32, uint8*);

	public:
		PGPokerHand*	hand;
		int32*			twocard;
		uint8			counts[14];
		uint8			suits[4];
};

void
OptimalPlayer::split(PGPokerHand* hand, int32* twocard)
{
	OPState s(hand, twocard);

	s.categorizeCards();
	s.splitHand();
}

void
OptimalPlayer::split(PGTilesHand* hand, int32* twocard)
{
	PGTilesHandRanks* pRanks[6] = { 0, 0, 0, 0, 0, 0 };
	
	twocard[0] = 0;
	twocard[1] = 1;
	if (hand->isSplitValid(twocard))
		pRanks[0] = new PGTilesHandRanks(hand, twocard);

	twocard[0] = 0;
	twocard[1] = 2;
	if (hand->isSplitValid(twocard))
		pRanks[1] = new PGTilesHandRanks(hand, twocard);

	twocard[0] = 0;
	twocard[1] = 3;
	if (hand->isSplitValid(twocard))
		pRanks[2] = new PGTilesHandRanks(hand, twocard);

	twocard[0] = 1;
	twocard[1] = 2;
	if (hand->isSplitValid(twocard))
		pRanks[3] = new PGTilesHandRanks(hand, twocard);

	twocard[0] = 1;
	twocard[1] = 3;
	if (hand->isSplitValid(twocard))
		pRanks[4] = new PGTilesHandRanks(hand, twocard);

	twocard[0] = 2;
	twocard[1] = 3;
	if (hand->isSplitValid(twocard))
		pRanks[5] = new PGTilesHandRanks(hand, twocard);
	
	int32 bestHigh = 0;
	int32 bestLow = 0;
	int32 i;

	for(i=1;i<6;i++)
	{
		if (pRanks[i])
		{
			if (!pRanks[bestHigh])
			{
				bestHigh = i;
				bestLow = i;
			}

			if (pRanks[i]->getHighRank() > pRanks[bestHigh]->getHighRank())
				bestHigh = i;

			if (pRanks[i]->getLowRank() > pRanks[bestLow]->getLowRank())
				bestLow = i;
		}
	}

	if (bestHigh == bestLow)		// we have an "Only Acceptable division"
	{
		switch (bestHigh)
		{
			case 0:	
				twocard[0] = 0;
				twocard[1] = 1;
				break;

			case 1:
				twocard[0] = 0;
				twocard[1] = 2;
				break;

			case 2:
				twocard[0] = 0;
				twocard[1] = 3;
				break;

			case 3:
				twocard[0] = 1;
				twocard[1] = 2;
				break;

			case 4:
				twocard[0] = 1;
				twocard[1] = 3;
				break;
				
			case 5:
				twocard[0] = 2;
				twocard[1] = 3;
				break;

			default:
				ASSERT ( false );
		}
	}
	else if (pRanks[bestHigh]->getHighRank() ==
		PGTilesHandRanks::HR_GEE_JOON)
	{
		/* for(i=0;i<6;i++)
		{
			if (pRanks[i] && 
				i != bestHigh &&
				i != bestLow)
			{
				altDiv = i;
			}
		}
		*/

		ASSERT ( false );
	}

	for(i=0;i<6;i++)
	{
		delete pRanks[i];
		pRanks[i] = 0;
	}
}

//
// -------------------------------- OPState -----------------------------------
//

OPState::OPState(PGPokerHand* hand, int32* twocard)
{
	this->hand = hand;
	this->twocard = twocard;

	twocard[0] = twocard[1] = -1;
}

void
OPState::categorizeCards()
{
	int32 i;

	// 2:index 0, 3:index 1..Ace:index 12, joker:13
	//
	memset(counts, 0, 13 + 1);
	memset(suits, 0, 4);

	for (i = 0; i < hand->getCount(); i++)
		if (!HPTR_ISJOKER_AT(i))
		{
			++HAS(HPTR_GETRANK_AT(i));
			++HASSUIT(HPTR_GETSUIT_AT(i));
		}
		else
			++HASJOKER();
}

void
OPState::splitHand()
{
	int32 start;
	bool8 reqJoker;

	if (hasStraight(&start, &reqJoker))		// Wong #6,8,10,12-15,17,18-20
		handleStraight(start);

	else if (hasFlush(&reqJoker))			// Wong #7,9,11,16, 18-20(no strght)
		handleFlush(reqJoker);

	else if (hasFullHouse())				// Wong #21,22
		handleFullHouse();

	else if (has_N_ofAKind(5, &reqJoker))	// Wong #27
		handle5ofAKind();

	else if (has_N_ofAKind(4, &reqJoker))	// Wong #24-26
		handle4ofAKind(reqJoker);

	else if (has_N_ofAKind(3, &reqJoker))	// Wong #5,23
		handle3ofAKind(reqJoker);

	else if (has_N_ofAKind(2, &reqJoker))	// Wong #2,3,4
		handleNPair();

	else									// Wong #1
		handleHighCard();
}

bool8
OPState::hasStraight(int32* startCard, bool8* usesJoker)
{
	// strategy:
	//		look for straight that doesn't rely upon joker (so we can
	//			potentially use joker elsewhere)
	//		fall back to straight with joker (if possible)
	//
	// NOTE:  hand may contain n-of-a-kinds also (not just singletons)
	//

	StraightIter iter(counts);

	if (iter.findStraight(false, true))
	{
		*startCard = iter.getStartRank();
		*usesJoker = false;
		return true;
	}

	if (HASJOKER() && iter.findStraight(true, true))
	{
		//
		// At this point we definitely have at least one straight.
		// 
		// Determine if joker is necessary for the straight (if its
		// within straightLength - 5 cards of either end, it's not
		// necessary).
		//
		if (iter.getWild() < iter.getProbe() - 5)		// wild on left end
		{
			*usesJoker = false;
			*startCard = RANKPTR(iter.getWild() + 1);
		}
		else						// wild on right end
		{
			*usesJoker = (bool8)(iter.getWild() - iter.getStart() <= 4);
			*startCard = iter.getStartRank();
		}

		return true;
	}

	return false;
}

bool8
OPState::hasFlush(bool8* usesJoker)
{
	// flush:
	//		5 or more cards same suit
	//		4 or more cards same suit and joker
	//
	uint8 largest;

	largest = MAX(suits[0], suits[1]);
	largest = MAX(largest, suits[2]);
	largest = MAX(largest, suits[3]);

	if (largest >= 5)
	{
		*usesJoker = false;
		return true;
	}
	if (HASJOKER() && largest >= 4)
	{
		*usesJoker = true;
		return true;
	}
	return false;
}

bool8
OPState::has_N_ofAKindNoJoker(int32 n)
{
	return (bool8)(has_ofAKind(n) != (uint8*)0);
}

bool8
OPState::has_N_ofAKindJoker(int32 n)
{
	return (bool8)(HASJOKER() && HASACE() == n - 1);
}

bool8
OPState::has_N_ofAKind(int32 n, bool8* usesJoker)
{
	// n-of-a-kind is:  n cards same rank or n-1 ACES and joker
	// 	(order below matters...attempt w/o relying on joker first)
	//
	if (has_N_ofAKindNoJoker(n))
	{
		*usesJoker = false;
		return true;
	}
	if (has_N_ofAKindJoker(n))
	{
		*usesJoker = true;
		return true;
	}
	return false;
}

bool8
OPState::hasFullHouse()
{
	uint8* probe;
	
	// full house:
	//		joker, 2 aces, 2 non-aces
	//		joker, 1 ace, 3 non-aces
	//		3 of a kind, pair (neither using joker)
	//
	probe = has_ofAKind(3);
	if (probe)
	{
		// 3 of any non-ace card, joker, at least 1 ace
		//
		if (RANKPTR(probe) != ACERANK && HASJOKER() && HASACE() > 0)
			return true;

		// 3 of any card, 2 of any other card...
		//
		if (has_ofAKind(2))
			return true;
	}

	// joker, at least 2 aces, 2 non-aces
	//
	if (HASJOKER() && HASACE() > 1)
	{
		probe = has_ofAKind(2);

		if (probe && RANKPTR(probe) != ACERANK)
			return true;
	}

	return false;
}

void
OPState::handleStraight(int32 start)
{
	bool8 flReqJoker, dummy;

	if (hasFlush(&flReqJoker))			// Wong #12,13,14,17,18,19,20
	{
		if (hasFullHouse())					// Wong #20
			handleStraightAndFlushAndFullHouse();

		else if (has_N_ofAKind(3, &dummy))	// Wong #19
			handleStraightAndFlushAnd3ofAKind();

		else if (has_N_ofAKind(2, &dummy))	// Wong #12,14,17,18
			handleStraightAndFlushAndNPair(start);
	
		else 								// Wong #13
			handleStraightAndFlush();
	}
	else									// Wong #6,8,10,15,18,19,20
	{
		if (hasFullHouse())					// Wong #20
			handleStraightAndFullHouse();
		
		else if (has_N_ofAKind(3, &dummy))	// Wong #19
			handleStraightAnd3ofAKind();

		else if (has_N_ofAKind(2, &dummy))	// Wong #8,10,15,18
			handleStraightAndNPair();

		else								// Wong #6
		{
			fiveCardTheStraight();

			DEBUGMSG(("...dealer had STRAIGHT  "));
		}
	}
}

void
OPState::handleStraightAndFlushAndFullHouse()
{
	// Wong #20:  uses *banker* optimal strategy
	//
	// assumes straight and flush and full house (maybe straight flush)
	//

	GetFullHouse fh(counts);

	if (fh.getThreeRank() >= ACERANK)
	{
		// twocard the pair
		//

		// Make sure we didn't erroneously get here:
		// if this assertion is true, we either should have two three of a kinds
		// (which isn't possible with a straight) or we should have more than
		// three aces (in which case we shouldn't be in this function AT ALL).
		//
		ASSERT(!fh.usedJokerForPair());

		twocard[0] = getCardIndex(fh.getPairRank(), 0);
		twocard[1] = getCardIndex(fh.getPairRank(), 1);
	}
	else
	{
		StraightFlushTest sft(hand, suits);
		int32 start;
		uint8 card;

		ASSERT(fh.usedJokerForPair());		// ACE-JOKER pair

		if (sft.hasStraightFlush(&start))
		{
			if (start == 10)
			{
				// fivecard the royal flush, twocard two from the 3-of-a-kind
				//
				twocard[0] = getCardIndex(fh.getThreeRank(), 0);
				twocard[1] = getCardIndex(fh.getThreeRank(), 1);

				ASSERT(twocard[0] != -1 && twocard[1] != -1 &&
					twocard[0] != twocard[1]);

				card = hand->getCardAt(twocard[0]);

				if (HPTR_GETSUIT(card) != sft.getFlushSuit())
				{
					card = hand->getCardAt(twocard[1]);

					if (HPTR_GETSUIT(card) == sft.getFlushSuit())
						twocard[1] = getCardIndex(fh.getThreeRank(), 2);
				}
				else
					twocard[0] = getCardIndex(fh.getThreeRank(), 2);
			}
			else
			{
				// fivecard the three of a kind, twocard the ACE-JOKER
				//
				ASSERT(start == ACERANK);	// no other possibility

				twocard[0] = getCardIndex(JOKERRANK, 0);
				twocard[1] = getCardIndex(ACERANK, 0);
			}
		}
		else
		{
			// ignore the flush and treat as straight and full house
			//
			if (fh.getThreeRank() >= JACKRANK)
			{
				// twocard two from the 3-of-a-kind, fivecard the straight
				//

				// Make sure we didn't erroneously get here:
				// if this assertion is true, we either should have three aces
				// (in which case, we should be in the code above) or we should
				// have 4 or more aces (in which case we shouldn't be in this
				// function AT ALL).
				//
				ASSERT(!fh.usedJokerForThree());

				twocard[0] = getCardIndex(fh.getThreeRank(), 0);
				twocard[1] = getCardIndex(fh.getThreeRank(), 1);
			}
			else
			{
				// The straight and full house hand requires an ACE and a JOKER
				//
				ASSERT(HASJOKER() && HASACE() > 0);

				twocard[0] = getCardIndex(JOKERRANK, 0);
				twocard[1] = getCardIndex(ACERANK, 0);
			}
		}
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had STRAIGHT and FLUSH and FULL HOUSE  "));
}

void
OPState::handleStraightAndFlushAnd3ofAKind()
{
	int32 fHigh[2], sHigh[2];
	bool8 useFlush;

	// Wong #19:  uses *banker* optimal strategy
	//
	// assumes straight, flush, and 3 of a kind; no pairs
	// (maybe straight flush)
	//
	// NOTE:  This code is *slightly* different from the
	//	  setStraightAndFlush_Wong13() code!  There are fewer exceptions.
	//

	fiveCardTheFlush();					// catches straight flush
	PGPokerHandRanks flushRank(hand, twocard, PGPokerHandRanks::LowRankOnly);

	twocard[0] = twocard[1] = -1;

	fiveCardTheStraight();				// might not catch a straight flush
	PGPokerHandRanks strRank(hand, twocard, PGPokerHandRanks::LowRankOnly);

	//
	// At this point the straight has been fivecarded...we only need to look
	// at the cases where we should fivecard the flush instead
	//
	useFlush = false;

	fHigh[0] = flushRank.getLowRank();
	sHigh[0] = strRank.getLowRank();

	if (fHigh[0] > sHigh[0])			// catches twocard pair
		useFlush = true;
	else if (fHigh[0] == sHigh[0])		// both have pair or high card
	{
		fHigh[0] = flushRank.getLowNthHighCard(0);
		sHigh[0] = strRank.getLowNthHighCard(0);

		if (fHigh[0] > sHigh[0])
			useFlush = true;
		else if (sHigh[0] == fHigh[0])
		{
			fHigh[1] = flushRank.getLowNthHighCard(1);
			sHigh[1] = strRank.getLowNthHighCard(1);

			if (fHigh[1] >= sHigh[1])	// catches straight flushes
				useFlush = true;
		}
	}
		
	if (useFlush)
	{
		twocard[0] = flushRank.getTwoCard(0);
		twocard[1] = flushRank.getTwoCard(1);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had STRAIGHT and FLUSH and 3 OF A KIND  "));
}

void
OPState::handleStraightAndFlushAndNPair(int32 start)
{
	// Wong #12,14,17,18: uses *banker* optimal strategy
	//
	GetNPair gnp(counts);

	ASSERT(gnp.getNumPair() >= 1 && gnp.getNumPair() <= 3);

	switch (gnp.getNumPair())
	{
		case 1:
		{
			StraightFlushTest sft(hand, suits);
			int32 start;

			if (gnp.usedJoker())
			{
				twocard[0] = getCardIndex(ACERANK, 0);
				twocard[1] = getCardIndex(JOKERRANK, 0);
			}
			else
			{
				twocard[0] = getCardIndex(gnp.getPairRank(0), 0);
				twocard[1] = getCardIndex(gnp.getPairRank(0), 1);
			}

			if (hasFlushWithout(twocard) || hasStraightWithout(twocard))
				break;

			twocard[0] = twocard[1] = -1;

			if (HASJOKER() && sft.hasStraightFlush(&start))
			{
				// Wong #12:  uses *banker's* optimal strategy
				//
				if (gnp.usedJoker())
					setJokerStraightFlushPair_JokerPair(&gnp);
				else
					setJokerStraightFlushPair_NonJokerPair(&gnp);
			}
			else
			{
				// Wong #14:  uses *banker's* optimal strategy
				//
				setStraightAndFlushAndPair(&gnp);
			}
			break;
		}

		case 2:
		{
			uint8* high[2];
			bool8 fiveCardTheStraightFlush;

			// Wong #17: uses *banker* optimal strategy
			//		1)  try to twocard a pair and still have a straight or flush
			//		2)	try the exceptions
			//		3)	use the table
			//
			twocard[0] = getCardIndex(gnp.getPairRank(0), 0);
			twocard[1] = getCardIndex(gnp.getPairRank(0), 1);

			if (hasFlushWithout(twocard) || hasStraightWithout(twocard))
				break;

			if (gnp.usedJoker())
			{
				twocard[0] = getCardIndex(ACERANK, 0);
				twocard[1] = getCardIndex(JOKERRANK, 0);
			}
			else
			{
				twocard[0] = getCardIndex(gnp.getPairRank(1), 0);
				twocard[1] = getCardIndex(gnp.getPairRank(1), 1);
			}

			if (hasFlushWithout(twocard) || hasStraightWithout(twocard))
				break;

			twocard[0] = twocard[1] = -1;

			//
			// exceptions first
			//
			fiveCardTheStraightFlush = false;

			if (start == 10 &&
				gnp.getPairRank(1) == ACERANK &&
				gnp.getPairRank(0) == KINGRANK)
				fiveCardTheStraightFlush = true;

			else if (start == 2 &&
				gnp.getPairRank(1) == 6 &&
				gnp.getPairRank(0) >= 4)
				fiveCardTheStraightFlush = true;
			
			else if (gnp.getPairRank(0) + gnp.getPairRank(1) <= 9)
				fiveCardTheStraightFlush = true;

			if (fiveCardTheStraightFlush)
			{
				int32 flushSuit;

				findMax(&flushSuit, suits, 4);
				flushSuit = NDXSUIT(flushSuit);

				twocard[0] = hand->findOffSuitIndex(flushSuit, 0);
				twocard[1] = hand->findOffSuitIndex(flushSuit, 1);
			}
			else
			{
				if (gnp.usedJoker())
					high[0] = has_ofAKind_R(1, counts, 13 - 1);
				else
					high[0] = has_ofAKind_R(1, counts, 13 + 1);

				high[1] = hasMore_ofAKind_R(1, high[0] - 1);

				setUsingTwoPairActionTable(gnp.getPair(), high);
			}
			break;
		}

		case 3:
			// Wong #18:  twocard highest pair (always ace & joker)
			//
			ASSERT(gnp.getPairRank(2) == ACERANK && gnp.usedJoker());

			twocard[0] = getCardIndex(ACERANK, 0);
			twocard[1] = getCardIndex(JOKERRANK, 0);
			break;
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had STRAIGHT and FLUSH and %d PAIR  ",
		gnp.getNumPair()));
}

void
OPState::handleStraightAndFlush()
{
	// Wong #13: uses *banker* optimal strategy
	//
	// Fivecard the straight and the flush in turn and determine
	// which yields the better twocard hand...first the flush,
	// then the straight.  Leave the straight fivecarded while
	// making the comparison (and only switch if flush is better).
	//

	fiveCardTheFlush();
	PGPokerHandRanks flushRank(hand, twocard, PGPokerHandRanks::LowRankOnly);

	twocard[0] = twocard[1] = -1;

	fiveCardTheStraight();
	PGPokerHandRanks strRank(hand, twocard, PGPokerHandRanks::LowRankOnly);

	setStraightAndFlush_Wong13(&flushRank, &strRank);

	DEBUGMSG(("...dealer had STRAIGHT and FLUSH  "));
}

void
OPState::handleStraightAndFullHouse()
{
	// Wong #20:  uses *banker* optimal strategy
	//
	// assumes straight and no flush
	//

	GetFullHouse fh(counts);

	if (fh.getThreeRank() >= ACERANK)
	{
		// twocard the pair
		//

		// Make sure we didn't erroneously get here:
		// if this assertion is true, we either should have two three of a kinds
		// (which isn't possible with a straight) or we should have more than
		// three aces (in which case we shouldn't be in this function AT ALL).
		//
		ASSERT(!fh.usedJokerForPair());

		twocard[0] = getCardIndex(fh.getPairRank(), 0);
		twocard[1] = getCardIndex(fh.getPairRank(), 1);
	}
	else if (fh.getThreeRank() >= JACKRANK)
	{
		// twocard two from the 3-of-a-kind, fivecard the straight
		//

		// Make sure we didn't erroneously get here:
		// if this assertion is true, we either should have three aces
		// (in which case, we should be in the code above) or we should
		// have 4 or more aces (in which case we shouldn't be in this
		// function AT ALL).
		//
		ASSERT(!fh.usedJokerForThree());

		twocard[0] = getCardIndex(fh.getThreeRank(), 0);
		twocard[1] = getCardIndex(fh.getThreeRank(), 1);
	}
	else
	{
		// The straight and full house hand requires an ACE and a JOKER
		//
		ASSERT(HASJOKER() && HASACE() > 0);

		twocard[0] = getCardIndex(JOKERRANK, 0);
		twocard[1] = getCardIndex(ACERANK, 0);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had STRAIGHT and FULL HOUSE  "));
}

void
OPState::handleStraightAnd3ofAKind()
{
	// Wong #19:  uses *banker* optimal strategy
	//
	// assumes straights and no flush and no full houses
	//
	// always fivecard the straight
	//

	fiveCardTheStraight();

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had STRAIGHT and 3 OF A KIND  "));
}

void
OPState::handleStraightAndNPair()
{
	uint8* high[2];

	// Wong #8,10,15,18
	//
	GetNPair gnp(counts);

	ASSERT(gnp.getNumPair() >= 1 && gnp.getNumPair() <= 3);

	switch (gnp.getNumPair())
	{
		case 1:
			if (HASJOKER())
			{
				// Wong #10:  uses *banker's* optimal strategy
				//
				if (gnp.usedJoker())
				{
					twocard[0] = getCardIndex(ACERANK, 0);
					twocard[1] = getCardIndex(JOKERRANK, 0);
				}
				else
				{
					twocard[0] = getCardIndex(gnp.getPairRank(0), 0);
					twocard[1] = getCardIndex(gnp.getPairRank(0), 1);
				}

				if (hasStraightWithout(twocard))
					break;

				twocard[0] = twocard[1] = -1;

				if (gnp.usedJoker())
					setJokerStraightPair_JokerPair(&gnp);
				else
					setJokerStraightPair_NonJokerPair(&gnp);
			}
			else
			{
				bool8 done = false;

				// Wong #8:  uses *banker's* optimal strategy
				//
				if (HASACE() && HAS(KINGRANK) && HAS(QUEENRANK) &&
					HAS(JACKRANK) && HAS(10))
				{
					switch (gnp.getPairRank(0))
					{
						case QUEENRANK:
						case JACKRANK:
						case 10:
							// fivecard the pair, twocard ACE & KING
							//
							twocard[0] = getCardIndex(ACERANK, 0);
							twocard[1] = getCardIndex(KINGRANK, 0);
							done = true;
							break;

						case KINGRANK:
							// fivecard the pair, twocard ACE & QUEEN
							//
							twocard[0] = getCardIndex(ACERANK, 0);
							twocard[1] = getCardIndex(QUEENRANK, 0);
							done = true;
							break;

						default:
							break;
					}
				}

				if (!done)
				{
					// fivecard the straight...however, first try to twocard
					// the pair if it gives a straight
					//
					twocard[0] = getCardIndex(gnp.getPairRank(0), 0);
					twocard[1] = getCardIndex(gnp.getPairRank(0), 1);

					if (!hasStraightWithout(twocard))
					{
						twocard[0] = twocard[1] = -1;

						fiveCardTheStraight();
					}
				}
			}
			break;

		case 2:
			// Wong #15:
			//		1)  try to twocard a pair and still have a straight
			//		2)	try the exceptions
			//		3)	use the table
			//
			twocard[0] = getCardIndex(gnp.getPairRank(0), 0);
			twocard[1] = getCardIndex(gnp.getPairRank(0), 1);

			if (hasStraightWithout(twocard))
				break;

			if (gnp.usedJoker())
			{
				twocard[0] = getCardIndex(ACERANK, 0);
				twocard[1] = getCardIndex(JOKERRANK, 0);
			}
			else
			{
				twocard[0] = getCardIndex(gnp.getPairRank(1), 0);
				twocard[1] = getCardIndex(gnp.getPairRank(1), 1);
			}

			if (hasStraightWithout(twocard))
				break;

			twocard[0] = twocard[1] = -1;

			// exceptions first
			//
			if (gnp.getPairRank(1) <= 4 &&
				gnp.getPairRank(0) == 2 &&
				!(HASACE() || HASJOKER()))
			{
				fiveCardTheStraight();
				break;
			}

			if (gnp.usedJoker())
				high[0] = has_ofAKind_R(1, counts, 13 - 1);
			else
				high[0] = has_ofAKind_R(1, counts, 13 + 1);

			high[1] = hasMore_ofAKind_R(1, high[0] - 1);

			setUsingTwoPairActionTable(gnp.getPair(), high);
			break;

		case 3:
			// Wong #18:  twocard highest pair (always ace & joker)
			//
			ASSERT(gnp.getPairRank(2) == ACERANK && gnp.usedJoker());

			twocard[0] = getCardIndex(ACERANK, 0);
			twocard[1] = getCardIndex(JOKERRANK, 0);
			break;
	}
	
	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had STRAIGHT and %d PAIR  ", gnp.getNumPair()));
}

void
OPState::fiveCardTheStraight()
{
	int32 start, straightLen;
	uint8 *wild, *other, *otherOther;

	// strategy:
	//	  make longest straight possible
	//	  if longer than 5 cards, make best *twocard* hand possible
	//	  if 5 card straight, twocard non-straight cards
	//
	// NOTE:  Doesn't care about suits or pairs (should be handled elsewhere).
	//
	//		  In other words, it does *NOT* make the best fivecard straight
	//		  AND twocard hand possible; only the best twocard hand.
	//
	//		  For example, it won't necessarily five card a straight flush when 
	//		  hand contains 5H 5C 5D 6H 7H 8H 9H; this case should be handled
	//		  elsewhere (it will make a straight and a pair, though).
	//

	straightLen = findLongestStraight(&start, &wild);

	ASSERT(straightLen >= 5);

	switch (straightLen)
	{
		case 7:
			if (HASJOKER())
			{
				ASSERT(wild != (uint8*)0);

				if (start == ACERANK)
				{
					if (RANKPTR(wild) == ACERANK)
					{
						twocard[0] = getCardIndex(JOKERRANK, 0);
						twocard[1] = getCardIndex(7, 0);
					}
					else if (RANKPTR(wild) == 2)
					{
						twocard[0] = getCardIndex(JOKERRANK, 0);
						twocard[1] = getCardIndex(start, 0);
					}
					else
					{
						twocard[0] = getCardIndex(start, 0);

						if (RANKPTR(wild) < 7)
							twocard[1] = getCardIndex(7, 0);
						else
							twocard[1] = getCardIndex(JOKERRANK, 0);
					}
				}
				else
				{
					if (RANKPTR(wild) == start)
					{
						twocard[0] = getCardIndex(JOKERRANK, 0);
						twocard[1] = getCardIndex(start + 6, 0);
					}
					else if (RANKPTR(wild) == start + 1)
					{
						twocard[0] = getCardIndex(JOKERRANK, 0);
						twocard[1] = getCardIndex(start, 0);
					}
					else if (RANKPTR(wild) < start + 5)
					{
						twocard[0] = getCardIndex(start + 6, 0);
						twocard[1] = getCardIndex(start + 5, 0);
					}
					else
					{
						twocard[0] = getCardIndex(JOKERRANK, 0);

						if (RANKPTR(wild) == start + 5)
							twocard[1] = getCardIndex(start + 6, 0);
						else
							twocard[1] = getCardIndex(start + 5, 0);
					}
				}
			}
			else
			{
				if (start == ACERANK)
				{
					twocard[0] = getCardIndex(ACERANK, 0);
					twocard[1] = getCardIndex(7, 0);
				}
				else
				{
					twocard[0] = getCardIndex(start + 6, 0);
					twocard[1] = getCardIndex(start + 5, 0);
				}
			}
			break;

		case 6:
			if (HASJOKER())
			{
				// Joker *MUST* be used for a 6-card straight; it's impossible
				// to make a 6 card straight without using a joker if the
				// hand contains a joker
				//
				ASSERT(wild != (uint8*)0);

				if (start == ACERANK)
				{
					if (RANKPTR(wild) == ACERANK)
						twocard[0] = getCardIndex(JOKERRANK, 0);
					else
						twocard[0] = getCardIndex(ACERANK, 0);

					other = findNthNonStraightCard(start, straightLen, 0);

					ASSERT(other != (uint8*)0);

					if (RANKPTR(other) != ACERANK)
						twocard[1] = getCardIndex(RANKPTR(other), 0);
					else
						twocard[1] = getCardIndex(RANKPTR(other), 1);
				}
				else
				{
					// if joker not necessary (on either end), use it and
					// other non-straight card
					//
					if (RANKPTR(wild) == start ||
						RANKPTR(wild) == start + 5)
						twocard[0] = getCardIndex(JOKERRANK, 0);
					else
						twocard[0] = getCardIndex(start + 5, 0);

					other = findNthNonStraightCard(start, straightLen, 0);

					ASSERT(other != (uint8*)0);

					if (RANKPTR(other) != start + 5)
						twocard[1] = getCardIndex(RANKPTR(other), 0);
					else
						twocard[1] = getCardIndex(RANKPTR(other), 1);
				}
			}
			else
			{
				if (start == ACERANK)
					twocard[0] = getCardIndex(ACERANK, 0);
				else
					twocard[0] = getCardIndex(start + 5, 0);

				other = findNthNonStraightCard(start, straightLen, 0);

				ASSERT(other != (uint8*)0);

				if ((start == ACERANK && RANKPTR(other) == start) ||
					(start != ACERANK && RANKPTR(other) == start + 5))
					twocard[1] = getCardIndex(RANKPTR(other), 1);
				else
					twocard[1] = getCardIndex(RANKPTR(other), 0);
			}
			break;

		case 5:
			other = findNthNonStraightCard(start, straightLen, 0);
			otherOther = findNthNonStraightCard(start, straightLen, 1);

			ASSERT(other != (uint8*)0 && otherOther != (uint8*)0);

			twocard[0] = getCardIndex(RANKPTR(other), 0);
			twocard[1] = getCardIndex(RANKPTR(otherOther),
				(other == otherOther ? 1 : 0));
			break;
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}


void
OPState::handleFlush(bool8 flushReqJoker)
{
	bool8 dummy;

	// Wong #7,9,11,16, 18-20 (no straight):  uses *banker* optimal strategy
	//
	// assumes no straights
	//

	if (hasFullHouse())						// Wong #20
		handleFlushAndFullHouse(flushReqJoker);

	else if (has_N_ofAKind(3, &dummy))		// Wong #19
		handleFlushAnd3ofAKind(flushReqJoker);

	else if (has_N_ofAKind(2, &dummy))		// Wong #9,11,16,18
		handleFlushAndNPair(flushReqJoker);

	else									// Wong #7
	{
		fiveCardTheFlush();

		DEBUGMSG(("...dealer had FLUSH  "));
	}
}

void
OPState::fiveCardTheFlush()
{
	int32 flushSize, flushSuit;

	flushSize = findMax(&flushSuit, suits, 4);
	flushSuit = NDXSUIT(flushSuit);

	if (HASJOKER())
		++flushSize;

	ASSERT(flushSize >= 5);

	switch (flushSize)
	{
		case 7:
			if (HASJOKER())
				twocard[0] = getCardIndex(JOKERRANK, 0);
			else
				twocard[0] = hand->findNthHighestSuitIndex(flushSuit, 0);

			twocard[1] = hand->findNthHighestSuitIndex(flushSuit, 1);
			break;

		case 6:
			if (HASJOKER())
				twocard[0] = getCardIndex(JOKERRANK, 0);
			else
				twocard[0] = hand->findNthHighestSuitIndex(flushSuit, 0);

			twocard[1] = hand->findOffSuitIndex(flushSuit, 0);
			break;

		case 5:
			twocard[0] = hand->findOffSuitIndex(flushSuit, 0);
			twocard[1] = hand->findOffSuitIndex(flushSuit, 1);
			break;
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::handleFlushAndFullHouse(bool8 flushReqJoker)
{
	// Wong #20:  uses *banker* optimal strategy
	//
	// assumes flush and no straights
	//

	GetFullHouse fh(counts);

	if (fh.getThreeRank() >= ACERANK)
	{
		// twocard the pair
		//

		// Make sure we didn't erroneously get here:
		// if this assertion is true, we either should have two three of a kinds
		// (which isn't possible with a flush) or we should have more than
		// three aces (in which case we shouldn't be in this function AT ALL).
		//
		ASSERT(!fh.usedJokerForPair());

		twocard[0] = getCardIndex(fh.getPairRank(), 0);
		twocard[1] = getCardIndex(fh.getPairRank(), 1);
	}
	else if (fh.getThreeRank() >= 10)
	{
		int32 flushSuit;

		// twocard two from the 3-of-a-kind, fivecard the flush
		//

		// Make sure we didn't erroneously get here:
		// if this assertion is true, we either should have three aces
		// (in which case, we should be in the code above) or we should
		// have 4 or more aces (in which case we shouldn't be in this
		// function AT ALL).
		//
		ASSERT(!fh.usedJokerForThree());

		findMax(&flushSuit, suits, 4);
		flushSuit = NDXSUIT(flushSuit);

		twocard[0] = hand->findOffSuitIndex(flushSuit, 0);
		twocard[1] = hand->findOffSuitIndex(flushSuit, 1);

		//
		// assert here that the twocard hand is the off-suit pair
		// (otherwise, no flush)
		//
	}
	else
	{
		// The flush and full house hand requires an ACE and a JOKER
		//
		ASSERT(HASJOKER() && HASACE() > 0);

		twocard[0] = getCardIndex(JOKERRANK, 0);
		twocard[1] = getCardIndex(ACERANK, 0);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had FLUSH and FULL HOUSE  "));
}

void
OPState::handleFlushAnd3ofAKind(bool8 flushReqJoker)
{
	// Wong #19:  uses *banker* optimal strategy
	//
	// assumes no straights or full houses
	//
	// always fivecard the flush
	//

	fiveCardTheFlush();

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had FLUSH and 3 OF A KIND  "));
}

void
OPState::handleFlushAndNPair(bool8 flushReqJoker)
{
	uint8 *high[2];

	// Wong #18,16,11,9:  uses *banker* optimal strategy
	//
	// assumes no straights, full houses, or 3 of a kinds
	//
	GetNPair gnp(counts);

	ASSERT(gnp.getNumPair() >= 1 && gnp.getNumPair() <= 3);

	switch (gnp.getNumPair())
	{
		case 1:
			// Wong #11,9
			//
			if (HASJOKER())
			{
				int32 flushSuit, nOfRank;

				// Wong #11:
				//
				if (gnp.usedJoker())
				{
					twocard[0] = getCardIndex(ACERANK, 0);
					twocard[1] = getCardIndex(JOKERRANK, 0);
				}
				else
				{
					twocard[0] = getCardIndex(gnp.getPairRank(0), 0);
					twocard[1] = getCardIndex(gnp.getPairRank(0), 1);
				}

				if (hasFlushWithout(twocard))
					break;

				twocard[0] = twocard[1] = -1;

				//
				// at this point, flushSize doesn't take into account the joker
				//
				if (!flushReqJoker)
				{
					fiveCardTheFlush();
					break;
				}

				findMax(&flushSuit, suits, 4);
				flushSuit = NDXSUIT(flushSuit);

				if (gnp.getPairRank(0) == ACERANK &&
					hand->contains(flushSuit, CardMgr::ACE) &&
					gnp.usedJoker())
				{
					int32 highSuit, highIndex;

					high[0] = findNthHighestCard(2, &nOfRank);

					ASSERT(high[0] != (uint8*)0);

					highIndex = getCardIndex(RANKPTR(high[0]), nOfRank);
					highSuit = HPTR_GETSUIT_AT(highIndex);

					if (RANKPTR(high[0]) == KINGRANK)
					{
						twocard[0] = highIndex;		// twocard the king
						twocard[1] = hand->findNthHighestOffSuitIndex(
							flushSuit, highSuit == flushSuit ? 0 : 1);
					}
					else if (RANKPTR(high[0]) == QUEENRANK &&
						highSuit == flushSuit)
					{
						twocard[0] = highIndex;

						high[1] = findNthHighestCard(3, &nOfRank);

						ASSERT(high[1] != (uint8*)0);

						highIndex = getCardIndex(RANKPTR(high[1]), nOfRank);
						highSuit = HPTR_GETSUIT_AT(highIndex);

						if (highSuit == flushSuit &&
							(RANKPTR(high[1]) == JACKRANK ||
								RANKPTR(high[1]) == 10))
							twocard[1] = highIndex;
						else
						{
							twocard[0] = -1;

							fiveCardTheFlush();
						}
					}
					else
						fiveCardTheFlush();

					break;
				}

				setUsingFlushPairNoJokerActionTableWithJoker(
					flushSuit, gnp.getPair(0));
			}
			else
			{
				int32 flushSize, flushSuit;

				// Wong #9:  uses *banker* optimal strategy
				//
				flushSize = findMax(&flushSuit, suits, 4);
				flushSuit = NDXSUIT(flushSuit);

				if (flushSize >= 6)
				{
					fiveCardTheFlush();
					break;
				}

				setUsingFlushPairNoJokerActionTable(flushSuit, gnp.getPair(0));
			}
			break;

		case 2:
			// Wong #16:
			//		1)  try to twocard a pair and still have a flush
			//		2)	try the exceptions
			//		3)	use the table
			//
			twocard[0] = getCardIndex(gnp.getPairRank(0), 0);
			twocard[1] = getCardIndex(gnp.getPairRank(0), 1);

			if (hasFlushWithout(twocard))
				break;

			if (gnp.usedJoker())
			{
				twocard[0] = getCardIndex(ACERANK, 0);
				twocard[1] = getCardIndex(JOKERRANK, 0);
			}
			else
			{
				twocard[0] = getCardIndex(gnp.getPairRank(1), 0);
				twocard[1] = getCardIndex(gnp.getPairRank(1), 1);
			}

			if (hasFlushWithout(twocard))
				break;

			twocard[0] = twocard[1] = -1;

			// exceptions first
			//
			if (gnp.usedJoker())
				high[0] = has_ofAKind_R(1, counts, 13 - 1);
			else
				high[0] = has_ofAKind_R(1, counts, 13 + 1);

			high[1] = hasMore_ofAKind_R(1, high[0] - 1);

			if ((gnp.getPairRank(1) + gnp.getPairRank(0) <= 7 &&
				 twoCardsAtMost(RANKPTR(high[0]), RANKPTR(high[1]),
					 QUEENRANK, 10)) ||

				(gnp.getPairRank(1) == ACERANK &&
					(gnp.getPairRank(0) == 2 ||
					 gnp.getPairRank(0) == KINGRANK)))
			{
				fiveCardTheFlush();
				break;
			}

			setUsingTwoPairActionTable(gnp.getPair(), high);
			break;

		case 3:
			// Wong #18:  twocard highest pair (always ace & joker)
			//
			ASSERT(gnp.getPairRank(2) == ACERANK && gnp.usedJoker());

			twocard[0] = getCardIndex(ACERANK, 0);
			twocard[1] = getCardIndex(JOKERRANK, 0);
			break;
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had FLUSH and %d PAIR  ", gnp.getNumPair()));
}

void
OPState::handleFullHouse()
{
	uint8 *three, *pair[2], *high[2];
	int32 usedJoker;

	// Wong #21,22:  uses *banker* optimal strategy
	//
	// assumes no straights, flushs, or anything other than a full house
	// (and 1 pair or 2 singletons)
	//

	usedJoker = 0;
	three = has_ofAKind(3);
	if (!three && HASJOKER() && HASACE() > 1)
	{
		three = counts + COUNTNDX(ACERANK);
		usedJoker = 3;
	}

	pair[0] = has_ofAKind(2);
	pair[1] = (uint8*)0;

	if (pair[0])
	{
		pair[1] = hasMore_ofAKind(2, pair[0] + 1);

		// check for pair of non-aces, pair of aces, joker and 2 singletons
		// (pair[1] will point to aces...which have already been used as
		// the three-of-a-kind)
		//
		if (pair[1] && usedJoker == 3 && RANKPTR(pair[1]) == ACERANK)
			pair[1] = (uint8*)0;

		if (!pair[1] &&
			RANKPTR(three) != ACERANK && RANKPTR(pair[0]) != ACERANK &&
			!usedJoker && HASJOKER() && HASACE() > 0)
		{
			pair[1] = counts + COUNTNDX(ACERANK);
			usedJoker = 2;
		}
	}
	else
	{
		if (RANKPTR(three) != ACERANK &&
			!usedJoker && HASJOKER() && HASACE() > 0)
		{
			pair[0] = counts + COUNTNDX(ACERANK);
			usedJoker = 1;
		}
	}

	ASSERT(three != (uint8*)0 && pair[0] != (uint8*)0);

	if (pair[1])		// full house + pair
	{
		if (RANKPTR(three) == ACERANK &&
			RANKPTR(pair[0]) == 2 &&
			RANKPTR(pair[1]) == 3)
		{
			// twocard two of the 3 aces
			//
			twocard[0] = getCardIndex(ACERANK, 0);	// two aces
			twocard[1] = getCardIndex(ACERANK, 1);	//  guaranteed
		}
		else
		{
			// twocard the higher pair
			//
			if (usedJoker == 2)
			{
				twocard[0] = getCardIndex(JOKERRANK, 0);
				twocard[1] = getCardIndex(ACERANK, 0);
			}
			else
			{
				twocard[0] = getCardIndex(RANKPTR(pair[1]), 0);
				twocard[1] = getCardIndex(RANKPTR(pair[1]), 1);
			}
		}
	}
	else				// full house and two singletons
	{
		if (usedJoker)
			high[0] = has_ofAKind_R(1, counts, 13 - 1);	//joker/ace used
		else
			high[0] = has_ofAKind_R(1, counts, 13 + 1); //allow joker

		high[1] = hasMore_ofAKind_R(1, high[0] - 1);

		setUsingFullHouseAndSingletonsActionTable(
			three, pair[0], high, (bool8)(usedJoker == 1));
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had FULL HOUSE  "));
}

void
OPState::handle5ofAKind()
{
	// Wong #27:  uses *banker* optimal strategy
	//
	// assumes no straights, flushs, or anything other than a five-of-a-kind
	// (and 1 pair or 2 singletons)
	//

	if (HAS(KINGRANK) == 2)
	{
		// twocard the pair of kings
		//
		twocard[0] = getCardIndex(KINGRANK, 0);
		twocard[1] = getCardIndex(KINGRANK, 1);
	}
	else
	{
		// twocard a pair of aces
		//
		twocard[0] = getCardIndex(ACERANK, 0);	// we have 4 aces and a joker
		twocard[1] = getCardIndex(ACERANK, 1);	//   guaranteed
	}
	
	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had 5 OF A KIND  "));
}

void
OPState::handle4ofAKind(bool8 reqJoker)
{
	uint8 *four=NULL, *three=NULL, *pair=NULL, *high[2];
	int32 usedJoker;

	// Wong #24,25,26:  uses *banker* optimal strategy
	//
	// assumes no straights, flushs, or anything other than a four-of-a-kind
	// (and 1 three-of-a-kind or 1 pair or 3 singletons).
	//

	usedJoker = 0;
	four = has_ofAKind(4);
	if (!four && HASJOKER() && HASACE() > 2)
	{
		four = counts + COUNTNDX(ACERANK);
		usedJoker = 4;
	}

	ASSERT(four != (uint8*)0);

	three = has_ofAKind(3);
	if (!three && RANKPTR(four) != ACERANK &&
		!usedJoker && HASJOKER() && HASACE() > 1)
	{
		three = counts + COUNTNDX(ACERANK);
		usedJoker = 3;
	}

	if (!three)
	{
		pair = has_ofAKind(2);
		if (!pair && RANKPTR(four) != ACERANK &&
			!usedJoker && HASJOKER() && HASACE() > 0)
		{
			pair = counts + COUNTNDX(ACERANK);
			usedJoker = 2;
		}
	}

	if (three)
	{
		if (ABS(RANKPTR(four) - RANKPTR(three)) == 1)
		{
			// fivecard the four-of-a-kind, twocard two from the three-of-a-kind
			//
			twocard[0] = getCardIndex(RANKPTR(three), 0);	// two non-jokers
			twocard[1] = getCardIndex(RANKPTR(three), 1);	//   guaranteed
		}
		else
		{
			// twocard two from the the higher rank
			//
			high[0] = MAX(four, three);

			twocard[0] = getCardIndex(RANKPTR(high[0]), 0);	// two non-jokers
			twocard[1] = getCardIndex(RANKPTR(high[0]), 1); //   guaranteed
		}
	}
	else if (pair)
	{
		if (four > pair && (RANKPTR(four) - RANKPTR(pair) >= 8))
		{
			// split up the four-of-a-kind
			//
			twocard[0] = getCardIndex(RANKPTR(four), 0);	// two non-jokers
			twocard[1] = getCardIndex(RANKPTR(four), 1);	//   guaranteed
		}
		else
		{
			// fivecard the four-of-a-kind, twocard the pair
			//
			if (usedJoker == 2)
			{
				twocard[0] = getCardIndex(JOKERRANK, 0);
				twocard[1] = getCardIndex(ACERANK, 0);
			}
			else
			{
				twocard[0] = getCardIndex(RANKPTR(pair), 0);
				twocard[1] = getCardIndex(RANKPTR(pair), 1);
			}
		}
	}
	else
	{
		if (usedJoker)
			high[0] = has_ofAKind_R(1, counts, 13 - 1);	//joker/ace used
		else
			high[0] = has_ofAKind_R(1, counts, 13 + 1); //allow joker

		high[1] = hasMore_ofAKind_R(1, high[0] - 1);

		setUsingFourOfAKindAndSingletonsActionTable(four, high);
	}
	
	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had 4 OF A KIND"));
}

void
OPState::handle3ofAKind(bool8 reqJoker)
{
	uint8 *three[2], *high[2];
	bool8 usedJoker;
	int32 i;

	// Wong #5,23:  uses *banker* optimal strategy
	//
	// assumes no straights, flushs, full-houses or anything other than
	// three-of-a-kinds (1 or 2 of them).
	//

	i = 0;
	usedJoker = false;
	three[i] = has_ofAKind(3);
	while (i < 2 && three[i])
	{
		if (++i >= 2)
			break;

		three[i] = hasMore_ofAKind(3, three[i - 1] + 1);
	}

	if (i < 2 && HASACE() > 1 && HASJOKER())
	{
		three[i++] = counts + COUNTNDX(ACERANK);
		usedJoker = true;
	}

	ASSERT(i >= 1 && i <= 2);

	switch (i)
	{
		case 1:
			// Wong #5
			//
			if (RANKPTR(three[0]) >= ACERANK)
			{
				high[0] = three[0];
				high[1] = has_ofAKind_R(1, counts, 13 - 1); // non-ace high card

				ASSERT(RANKPTR(high[1]) < ACERANK);

				twocard[0] = getCardIndex(RANKPTR(high[0]), 0);
				twocard[1] = getCardIndex(RANKPTR(high[1]), 0);
			}
			else if (RANKPTR(three[0]) == KINGRANK)
			{
				ASSERT(!usedJoker);

				high[0] = has_ofAKind_R(1, counts, 13 + 1);		// allow joker
				high[1] = hasMore_ofAKind_R(1, high[0] - 1);

				if (twoCardsAtLeast(RANKPTR(high[0]), RANKPTR(high[1]),
					JACKRANK, 10))
				{
					// fivecard the 3-of-a-kind, twocard two highest singletons
					//
					twocard[0] = getCardIndex(RANKPTR(high[0]), 0);
					twocard[1] = getCardIndex(RANKPTR(high[1]), 0);
				}
				else
				{
					// twocard one from the 3-of-a-kind and highest singleton
					//
					twocard[0] = getCardIndex(RANKPTR(three[0]), 0);
					twocard[1] = getCardIndex(RANKPTR(high[0]), 0);
				}
			}
			else
			{
				if (usedJoker)
					high[0] = has_ofAKind_R(1, counts, 13 - 1);	//joker/ace used
				else
					high[0] = has_ofAKind_R(1, counts, 13 + 1); //allow joker

				high[1] = hasMore_ofAKind_R(1, high[0] - 1);
				
				twocard[0] = getCardIndex(RANKPTR(high[0]), 0);
				twocard[1] = getCardIndex(RANKPTR(high[1]), 0);
			}
			break;

		case 2:
			// Wong #23
			//
			twocard[0] = getCardIndex(RANKPTR(three[1]), 0);
			twocard[1] = getCardIndex(RANKPTR(three[1]), 1);
			break;
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had 3 OF A KIND  "));
}

void
OPState::handleNPair()
{
	uint8 *high[2];

	// Wong #2,3,4:  uses *banker* optimal strategy
	//
	// assumes no straights, flushs, or anything other than pairs
	// (1, 2, or 3 of them).
	//

	GetNPair gnp(counts);

	ASSERT(gnp.getNumPair() >= 1 && gnp.getNumPair() <= 3);

	switch (gnp.getNumPair())
	{
		case 1:
			// Wong #2:  fivecard pair, twocard the two highest singletons
			//
			if (gnp.usedJoker())
				high[0] = has_ofAKind_R(1, counts, 13 - 1);	// joker/ace used
			else
				high[0] = has_ofAKind_R(1, counts, 13 + 1); // allow joker

			high[1] = hasMore_ofAKind_R(1, high[0] - 1);

			twocard[0] = getCardIndex(RANKPTR(high[0]), 0);
			twocard[1] = getCardIndex(RANKPTR(high[1]), 0);
			break;

		case 2:
			// Wong #3
			//
			if (gnp.usedJoker())
				high[0] = has_ofAKind_R(1, counts, 13 - 1);	// joker/ace used
			else
				high[0] = has_ofAKind_R(1, counts, 13 + 1); // allow joker

			high[1] = hasMore_ofAKind_R(1, high[0] - 1);

			setUsingTwoPairActionTable(gnp.getPair(), high);
			break;

		case 3:
			// Wong #4:  twocard the highest pair
			//
			twocard[0] = getCardIndex(gnp.getPairRank(2), 0);

			if (gnp.getPairRank(2) == ACERANK && gnp.usedJoker())
				twocard[1] = getCardIndex(JOKERRANK, 0);
			else
				twocard[1] = getCardIndex(gnp.getPairRank(2), 1);
			break;
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had %d PAIR  ", gnp.getNumPair()));
}

void
OPState::handleHighCard()
{
	uint8* high[4];

	// Wong #1:  uses *banker* optimal strategy
	//
	// assumes there really are no pairs, etc
	//

	high[0] = skipZeros_R(counts, 13 + 1);	// include joker in search
	high[1] = skipMoreZeros_R(high[0] - 1);
	high[2] = skipMoreZeros_R(high[1] - 1);
	high[3] = skipMoreZeros_R(high[2] - 1);

	if (RANKPTR(high[0]) >= ACERANK &&	  // ace or joker (counts as ace)
		RANKPTR(high[1]) >= QUEENRANK &&  // queen or king (not ace..else pair)
		RANKPTR(high[2]) >= 8 && RANKPTR(high[2]) <= 10 &&
		RANKPTR(high[3]) >= 7 && RANKPTR(high[3]) <= 8)
	{
		twocard[0] = getCardIndex(RANKPTR(high[1]), 0);	// 2nd & 4rd highest
		twocard[1] = getCardIndex(RANKPTR(high[3]), 0);
	}
	else
	{
		twocard[0] = getCardIndex(RANKPTR(high[1]), 0);	// 2nd & 3rd highest
		twocard[1] = getCardIndex(RANKPTR(high[2]), 0);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);

	DEBUGMSG(("...dealer had HIGHCARD  "));
}

int32
OPState::getCardIndex(int32 rank, int32 nOfRank)
{
	if (rank != JOKERRANK)
		return hand->findNonJokerIndex(rank, nOfRank);
	else
		return hand->findJokerIndex();
}

uint8*
OPState::findNthHighestCard(int32 nOfHand, int32* nOfRank)
{
	uint8* probe;

	probe = skipZeros_R(counts, 13 + 1);		// includes joker
	while (probe)
	{
		if (nOfHand - *probe < 0)
		{
			*nOfRank = nOfHand;
			return probe;
		}

		nOfHand -= *probe;

		probe = skipMoreZeros_R(probe - 1);
	}

	return (uint8*)0;
}

uint8*
OPState::findNthNonStraightCard(int32 start, int32 straightLen, int32 n)
{
	uint8* probe;

	if (start == ACERANK)
	{
		// look for singletons with rank > 5 && rank <= KING
		// look for n-of-a-kinds with rank <= 5 || rank == ACE
		//

		probe = skipZeros_R(counts, 13);	// joker not included
		while (probe)
		{
			if (RANKPTR(probe) > straightLen && RANKPTR(probe) <= KINGRANK)
			{
				if (n - *probe < 0)
					return probe;

				n -= *probe;
			}
			else if (*probe > 1)
			{
				if (n - (*probe - 1) < 0)
					return probe;

				n -= *probe - 1;
			}

			probe = skipMoreZeros_R(probe - 1);
		}
	}
	else
	{
		// look for singletons with rank < start || rank >= start + 5
		// look for n-of-a-kinds with rank >= start && rank < start + 5
		//
		probe = skipZeros_R(counts, 13);	// joker not included
		while (probe)
		{
			if (RANKPTR(probe) < start || RANKPTR(probe) >= start + straightLen)
			{
				if (n - *probe < 0)
					return probe;

				n -= *probe;
			}
			else if (*probe > 1)
			{
				if (n - (*probe - 1) < 0)
					return probe;

				n -= *probe - 1;
			}

			probe = skipMoreZeros_R(probe - 1);
		}
	}

	return (uint8*)0;
}

int32
OPState::findLongestStraight(int32* MaxStart, uint8** MaxWild)
{
	int32 maxLen;
	StraightIter iter(counts);

	*MaxStart = -1;
	*MaxWild = (uint8*)0;
	maxLen = 0;

	if (iter.findStraight(true, true))
	{
		do
		{
			if (iter.getLen() > maxLen)
			{
				maxLen = iter.getLen();
				*MaxStart = iter.getStartRank();
				*MaxWild = iter.getWild();
			}
		} while (iter.findStraight(true, false));
	}
	return maxLen;
}

bool8
OPState::hasFlushWithout(int32* twoCard)
{
	uint8 tempSuits[4];
	int32 i, largest, suitNdx;
	bool8 jokerAvailable;

	memset(tempSuits, 0, 4);

	largest = 0;
	jokerAvailable = false;

	for (i = 0; i < hand->getCount(); i++)
	{
		if (i == twoCard[0] || i == twoCard[1])
			continue;

		if (!HPTR_ISJOKER_AT(i))
		{
			suitNdx = SUITNDX(HPTR_GETSUIT_AT(i));

			if (++tempSuits[suitNdx] > largest)
				largest = tempSuits[suitNdx];
		}
		else
			jokerAvailable = true;
	}

	return (bool8)(largest >= 5 || (jokerAvailable && largest >= 4));
}

bool8
OPState::hasStraightWithout(int32* twoCard)
{
	uint8 tempCounts[14];
	int32 i;

	memset(tempCounts, 0, 13 + 1);

	for (i = 0; i < hand->getCount(); i++)
	{
		if (i == twoCard[0] || i == twoCard[1])
			continue;

		if (!HPTR_ISJOKER_AT(i))
			++tempCounts[COUNTNDX(HPTR_GETRANK_AT(i))];
		else
			++tempCounts[COUNTNDX(JOKERRANK)];
	}

	StraightIter iter(tempCounts);

	return iter.findStraight(true, true);
}

void
OPState::setStraightAndFlush_Wong13(
	PGPokerHandRanks* flushRank, PGPokerHandRanks* strRank)
{
	int32 fHigh[2], sHigh[2];
	bool8 useFlush;

	// Wong #13: uses *banker* optimal strategy
	//
	// At this point the straight has been fivecarded...we only need to look
	// at the cases where we should fivecard the flush instead
	//
	useFlush = false;

	fHigh[0] = flushRank->getLowRank();
	sHigh[0] = strRank->getLowRank();

	if (fHigh[0] > sHigh[0])			// catches twocard pair
		useFlush = true;
	else if (fHigh[0] == sHigh[0])		// both have pair or high card
	{
		fHigh[0] = flushRank->getLowNthHighCard(0);
		sHigh[0] = strRank->getLowNthHighCard(0);

		if (fHigh[0] > sHigh[0])
			useFlush = true;
		else if (sHigh[0] > fHigh[0])
		{
			if (sHigh[0] < JACKRANK)
				useFlush = true;
		}
		else	// look at low twocard
		{
			fHigh[1] = flushRank->getLowNthHighCard(1);
			sHigh[1] = strRank->getLowNthHighCard(1);

			if (fHigh[1] >= sHigh[1])	// catches straight flushes
				useFlush = true;
			else
			{
				if (sHigh[0] == ACERANK)
				{
					if (sHigh[1] <= 10)
						useFlush = true;
				}
				else
				{
					if (sHigh[1] <= 8)
						useFlush = true;
				}
			}
		}
	}

	if (useFlush)
	{
		twocard[0] = flushRank->getTwoCard(0);
		twocard[1] = flushRank->getTwoCard(1);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setJokerStraightPair_JokerPair(GetNPair* gnp)
{
	uint8* high;

	// determine if we can make a straight without the ace
	//
	--HASACE();
	
	StraightIter iter(counts);

	if (iter.findStraight(true, true))
	{
		twocard[0] = getCardIndex(ACERANK, 0);

		high = findNthNonStraightCard(iter.getStartRank(), 5, 0);

		ASSERT(high != (uint8*)0);
		twocard[1] = getCardIndex(RANKPTR(high), 0);
	}
	else
	{
		int32 nOfRank;

		++HASACE();

		// fivecard the ace, joker, and 3 lowest singletons
		//
		high = findNthHighestCard(2, &nOfRank);
		ASSERT(high != (uint8*)0);
		twocard[0] = getCardIndex(RANKPTR(high), nOfRank);

		high = findNthHighestCard(3, &nOfRank);
		ASSERT(high != (uint8*)0);
		twocard[1] = getCardIndex(RANKPTR(high), nOfRank);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setJokerStraightPair_NonJokerPair(GetNPair* gnp)
{
	int32 start, head;
	uint8 *wild, *other;
	bool8 fiveCardThePair;

	findLongestStraight(&start, &wild);

	other = findNthNonStraightCard(start, 5, 0);
	ASSERT(other != (uint8*)0);

	if (RANKPTR(other) == gnp->getPairRank(0))
	{
		other = findNthNonStraightCard(start, 5, 1);
		ASSERT(other != (uint8*)0);
	}

	if (start == ACERANK)
		head = 5;
	else
		head = start + 5 - 1;

	fiveCardThePair = false;

	if (gnp->getPairRank(0) == KINGRANK && HAS(QUEENRANK) == 0)
		/* fivecard the straight */ ;
	else
	{
		if (start == 9)		// straight headed by king
			fiveCardThePair = true;
		else if (RANKPTR(other) <= head + 1)
		{
			if (start >= 7 && gnp->getPairRank(0) >= 7)
				fiveCardThePair = true;
			else if (start == 6 && gnp->getPairRank(0) >= 9)
				fiveCardThePair = true;
		}
	}

	if (fiveCardThePair)
		fiveCardTheOnlyPair(gnp);
	else
	{
		// fivecard the straight
		//
		twocard[0] = getCardIndex(RANKPTR(other), 0);
		twocard[1] = getCardIndex(gnp->getPairRank(0), 0);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setJokerStraightFlushPair_JokerPair(GetNPair* gnp)
{
	enum NoAcePossibilities { STRAIGHT_POSSIBLE = 0x01, FLUSH_POSSIBLE=0x02 };
	int32 noace, aceIndex, aceSuit;
	bool8 strReqJoker, flushReqJoker;
	int32 start, nOfRank;
	uint8 *high;


	ASSERT(HASACE() && HASJOKER());

	// determine if we can make a straight or flush without the ace
	//
	--HASACE();

	aceIndex = getCardIndex(ACERANK, 0);
	ASSERT(aceIndex != -1);
	aceSuit = HPTR_GETSUIT_AT(aceIndex);
	--HASSUIT(aceSuit);

	noace = 0;

	if (hasStraight(&start, &strReqJoker))
		noace |= STRAIGHT_POSSIBLE;

	if (hasFlush(&flushReqJoker))
		noace |= FLUSH_POSSIBLE;

	if (noace)
	{
		int32 flushSuit;

		findMax(&flushSuit, suits, 4);
		flushSuit = NDXSUIT(flushSuit);

		if (noace == (STRAIGHT_POSSIBLE | FLUSH_POSSIBLE))	// both possible
		{
			// Fivecard the straight and the flush in turn and determine
			// which yields the better twocard hand...first the flush,
			// then the straight.  Leave the straight fivecarded while
			// making the comparison (and only switch if flush is better).
			//

			twocard[0] = aceIndex;
			twocard[1] = hand->findNthHighestOffSuitIndex(
				flushSuit, aceSuit == flushSuit ? 0 : 1);

			PGPokerHandRanks flushRank(hand, twocard,
				PGPokerHandRanks::LowRankOnly);

			twocard[0] = aceIndex;
			high = findNthNonStraightCard(start, 5, 0);
			ASSERT(high != (uint8*)0);
			twocard[1] = getCardIndex(RANKPTR(high), 0);
			
			PGPokerHandRanks strRank(hand, twocard,
				PGPokerHandRanks::LowRankOnly);

			++HASACE();
			++HASSUIT(aceSuit);

			setStraightAndFlush_Wong13(&flushRank, &strRank);
		}
		else if (noace & STRAIGHT_POSSIBLE)
		{
			// fivecard the straight, twocard the ace and odd singleton
			//
			// NOTE:  the ace has already been taken from counts[]
			//
			twocard[0] = aceIndex;

			high = findNthNonStraightCard(start, 5, 0);
			ASSERT(high != (uint8*)0);
			twocard[1] = getCardIndex(RANKPTR(high), 0);
		}
		else
		{
			// fivecard the flush, twocard the ace and odd singleton
			//
			// NOTE:  the ace's suit has already been taken from suits[];
			//	  however, the code doesn't rely upon suits[]
			//	  (it calls hand->...)
			//
			twocard[0] = aceIndex;
			twocard[1] = hand->findNthHighestOffSuitIndex(
				flushSuit, aceSuit == flushSuit ? 0 : 1);
		}
	}
	else
	{
		++HASACE();
		++HASSUIT(aceSuit);

		// fivecard the ace and joker and 3 lowest singletons
		//
		high = findNthHighestCard(2, &nOfRank);
		ASSERT(high != (uint8*)0);
		twocard[0] = getCardIndex(RANKPTR(high), nOfRank);

		high = findNthHighestCard(3, &nOfRank);
		ASSERT(high != (uint8*)0);
		twocard[1] = getCardIndex(RANKPTR(high), nOfRank);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setJokerStraightFlushPair_NonJokerPair(GetNPair* gnp)
{
	int32 start, head;
	uint8 *wild, *other;
	bool8 fiveCardThePair;

	findLongestStraight(&start, &wild);

	other = findNthNonStraightCard(start, 5, 0);
	ASSERT(other != (uint8*)0);

	if (RANKPTR(other) == gnp->getPairRank(0))
	{
		other = findNthNonStraightCard(start, 5, 1);
		ASSERT(other != (uint8*)0);
	}

	if (start == ACERANK)
		head = 5;
	else
		head = start + 5 - 1;

	fiveCardThePair = false;

	if (gnp->getPairRank(0) == KINGRANK)
		/* fivecard the straight flush */ ;
	else
	{
		if (start == 9)		// straight headed by king
			fiveCardThePair = true;
		else if (RANKPTR(other) <= head + 1)
		{
			if (start == 8 && gnp->getPairRank(0) >= 8)
				fiveCardThePair = true;
			else if (start == 7 && gnp->getPairRank(0) >= 9)
				fiveCardThePair = true;
		}
	}

	if (fiveCardThePair)
		fiveCardTheOnlyPair(gnp);
	else
	{
		int32 flushSuit;

		// fivecard the straight flush
		//
		findMax(&flushSuit, suits, 4);
		flushSuit = NDXSUIT(flushSuit);

		twocard[0] = getCardIndex(RANKPTR(other), 0);

		twocard[1] = getCardIndex(gnp->getPairRank(0), 0);
		ASSERT(twocard[1] != -1);
		if (HPTR_GETSUIT_AT(twocard[1]) == flushSuit)
			twocard[1] = getCardIndex(gnp->getPairRank(0), 1);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setStraightAndFlushAndPair(GetNPair* gnp)
{
	PGPokerHandRanks* winner;
	int32 lucky;

	// Wong #14:  uses *banker's* optimal strategy
	//
	// Assumes no straight flush, no 3-of-a-kind or greater, only one pair
	//
	// Strategy:  compare the 3 following methods to determine the best:
	//		1) straight, ignoring flush and pair
	//		2) flush, ignoring straight and pair
	//		3) pair, ignoring straight and flush
	//
	fiveCardTheStraight();
	PGPokerHandRanks strRank(hand, twocard);

	twocard[0] = twocard[1] = -1;

	fiveCardTheFlush();					// catches straight flush
	PGPokerHandRanks flushRank(hand, twocard);

	twocard[0] = twocard[1] = -1;

	fiveCardTheOnlyPair(gnp);
	PGPokerHandRanks pairRank(hand, twocard);

	twocard[0] = twocard[1] = -1;

	// favor the flush in ties
	//
	lucky = compareHandRanks(&strRank, &flushRank);
	winner = (PGPokerHandRanks*)(lucky == 1 ? &strRank : &flushRank);

	lucky = compareHandRanks(&pairRank, winner);
	if (lucky == 1)
		winner = &pairRank;

	twocard[0] = winner->getTwoCard(0);
	twocard[1] = winner->getTwoCard(1);

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

int32
OPState::compareHandRanks(PGPokerHandRanks* r1, PGPokerHandRanks* r2)
{
	int32 lo, hi;

	//
	// Compare each hand for winner then determine winner...
	//	 (no special "tie" rules, unlike player vs. dealer hands)
	// 

	lo = r1->compareLowRanks(r2);
	hi = r1->compareHighRanks(r2);

	//
	// Outcome is determined as follows:
	//		if both r2 hands beat both r1 hands, the r2 wins
	//		if both r1 hands beat both r2 hands, the r1 wins
	//		otherwise they're equivalent
	//

	if (lo > 0 && hi > 0)
		return 1;

	if (lo < 0 && hi < 0)
		return -1;

	if (hi == 0)
		return lo;

	if (lo == 0)
		return hi;

	//
	// at this point, one of the following must hold:
	//	  1) r1 beats r2 in low; r2 beats r1 in high
	//	  2) r2 beats r1 in low; r1 beats r2 in high
	//
	// low hand is most important

	return lo;
}

void
OPState::fiveCardTheOnlyPair(GetNPair* gnp)
{
	uint8* high;
	int32 i, n, nOfRank, rank;

	// twocard the two highest, non-pair cards
	//
	for (i = n = 0; i < 2; n++)
	{
		high = findNthHighestCard(n, &nOfRank);

		rank = RANKPTR(high);		// change jokers to aces
		if (rank == JOKERRANK)		//   for comparison (only)
			rank = ACERANK;

		if (rank != gnp->getPairRank(0))
			twocard[i++] = getCardIndex(RANKPTR(high), nOfRank);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setUsingTwoPairActionTable(
	uint8* pair[3], uint8* high[2])
{
	uint8 action;
	
	action = sTwoPairAction[pair[1] - counts][pair[0] - counts];

	ASSERT(action != 0);

	if (action != 0xff &&
		twoCardsAtLeast(RANKPTR(high[0]), RANKPTR(high[1]),
			(action >> 4) & 0x0f, action & 0x0f))
	{
		// fivecard the two pair
		//
		twocard[0] = getCardIndex(RANKPTR(high[0]), 0);
		twocard[1] = getCardIndex(RANKPTR(high[1]), 0);
	}
	else
	{
		// twocard the low pair
		//
		ASSERT(RANKPTR(pair[0]) < ACERANK);

		twocard[0] = getCardIndex(RANKPTR(pair[0]), 0);
		twocard[1] = getCardIndex(RANKPTR(pair[0]), 1);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setUsingFourOfAKindAndSingletonsActionTable(
	uint8* four, uint8* high[2])
{
	uint8 action;

	action = sFourOfAKindAndSingletonsAction[four - counts];

	if (action == 0 ||
		(action != 0xff &&
		twoCardsAtLeast(RANKPTR(high[0]), RANKPTR(high[1]),
			(action >> 4) & 0x0f, action & 0x0f)))
	{
		// fivecard the four-of-a-kind
		//
		twocard[0] = getCardIndex(RANKPTR(high[0]), 0);
		twocard[1] = getCardIndex(RANKPTR(high[1]), 0);
	}
	else
	{
		// fivecard two of the four, twocard the other two
		//
		twocard[0] = getCardIndex(RANKPTR(four), 0);
		twocard[1] = getCardIndex(RANKPTR(four), 1);
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setUsingFullHouseAndSingletonsActionTable(
	uint8* three, uint8* pair, uint8* high[2], bool8 pairUsesJoker)
{
	uint8 action;

	action = sFullHouseAndSingletonsAction[three - counts][pair - counts];

	ASSERT(action != 0);

	if (action != 0xff &&
		twoCardsAtLeast(RANKPTR(high[0]), RANKPTR(high[1]),
			(action >> 4) & 0x0f, action & 0x0f))
	{
		// fivecard the full house
		//
		twocard[0] = getCardIndex(RANKPTR(high[0]), 0);
		twocard[1] = getCardIndex(RANKPTR(high[1]), 0);
	}
	else
	{
		// twocard the pair from the full house
		//
		if (pairUsesJoker)
		{
			twocard[0] = getCardIndex(JOKERRANK, 0);
			twocard[1] = getCardIndex(ACERANK, 0);
		}
		else
		{
			twocard[0] = getCardIndex(RANKPTR(pair), 0);
			twocard[1] = getCardIndex(RANKPTR(pair), 1);
		}
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setUsingFlushPairNoJokerActionTable(int32 flushSuit, uint8* pair)
{
	int32 i, n, highIndex[2], high[2], offSingleton;
	uint8 action;
	uint8 card;

	//
	// Assumes no jokers and the flush is 5 cards long (specifically,
	// not 6 cards long).  Guaranteed to have 1 non-pair, offsuit singleton.
	//

	// find two highest non-pair singletons from flushSuit
	//
	i = n = 0;
	while (i < 2)
	{
		highIndex[i] = hand->findNthHighestSuitIndex(flushSuit, n);

		card = hand->getCardAt(highIndex[i]);

		ASSERT(card != 0xff && !HPTR_ISJOKER(card));

		high[i] = HPTR_GETRANK(card);

		if (RANKPTR(pair) != high[i])
			++i;

		++n;
	}

	if (high[0] < ACERANK)
		fiveCardTheFlush();
	else
	{
		// find highest singleton NOT from the flushSuit
		//
		card = hand->getCardAt(hand->findNthHighestOffSuitIndex(flushSuit, 0));

		ASSERT(card != 0xff && !HPTR_ISJOKER(card));

		offSingleton = HPTR_GETRANK(card);

		action = sFlushPairNoJokerAction[COUNTNDX(high[1])][pair - counts];

		ASSERT(action != 0);

		if (action == 1)
		{
			// fivecard the pair, breaking up the flush
			//
			twocard[0] = highIndex[0];
			twocard[1] = highIndex[1];
		}
		else if (action == 0xff || offSingleton > action)
			fiveCardTheFlush();
		else
		{
			// break up the flush
			//
			twocard[0] = highIndex[0];
			twocard[1] = highIndex[1];
		}
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}

void
OPState::setUsingFlushPairNoJokerActionTableWithJoker(
	int32 flushSuit, uint8* pair)
{
	int32 n=0, highIndex=0, high=0, offSingleton=0;
	uint8 action;
	uint8 card;

	// find highest non-pair singleton from flushSuit (joker is first high)
	//
	while (n < PGPokerHand::MAXCARDS)
	{
		highIndex = hand->findNthHighestSuitIndex(flushSuit, n);

		card = hand->getCardAt(highIndex);

		ASSERT(card != 0xff && !HPTR_ISJOKER(card));

		high = HPTR_GETRANK(card);

		if (RANKPTR(pair) != high)
			break;

		++n;
	}

	ASSERT(n < PGPokerHand::MAXCARDS);

	if (high == ACERANK)
		fiveCardTheFlush();
	else
	{
		// find highest singleton NOT from the flushSuit
		//
		n = 0;
		while (n < PGPokerHand::MAXCARDS)
		{
			card = hand->getCardAt(
				hand->findNthHighestOffSuitIndex(flushSuit, n));

			ASSERT(card != 0xff);

			if (!HPTR_ISJOKER(card))
			{
				offSingleton = HPTR_GETRANK(card);

				break;
			}

			++n;
		}

		ASSERT(n < PGPokerHand::MAXCARDS);

		action = sFlushPairNoJokerAction[COUNTNDX(high)][pair - counts];

		ASSERT(action != 0);

		if (action == 1)
		{
			// fivecard the pair, breaking up the flush
			//
			twocard[0] = getCardIndex(JOKERRANK, 0);
			twocard[1] = highIndex;
		}
		else if (action == 0xff || offSingleton > action)
			fiveCardTheFlush();
		else
		{
			// break up the flush
			//
			twocard[0] = getCardIndex(JOKERRANK, 0);
			twocard[1] = highIndex;
		}
	}

	ASSERT(twocard[0] != -1 && twocard[1] != -1 && twocard[0] != twocard[1]);
}



